package com.tangsm.spring.boot.validation.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import java.util.Optional;
import java.util.Set;

/**
 * 数据校验工具类
 * <p>为什么要使用这个工具类：</p>
 * <p>controller方法中不用加入BindingResult参数</p>
 * <p>controller方法中需要校验的参数也不需要加入@Valid或者@Validated注解</p>
 * <p>具体使用：</p>
 * <p>1、在controller方法调用 ValidatorUtils.validate(需要校验的Bean) 直接获取校验的结果。</p>
 * <p>2、在全局拦截校验器中调用 ValidatorUtils.validate(需要校验的Bean) 直接获取校验的结果。</p>
 * @author tangsm
 */
@Component
public class ValidatorUtils {
    private static final Logger log = LoggerFactory.getLogger(ValidatorUtils.class);
    /**
     * 验证数据
     * @param value 需要验证数据对像
     */
    public static Optional<String> validate(Object value)  {
        // 验证数据对像上的所有约束，如果没有约束违反或空集
        Set<ConstraintViolation<Object>> results = Validation.buildDefaultValidatorFactory()
                .getValidator().validate(value);

        // 没有约束违反直接返回空的Optional实例
        if (CollectionUtils.isEmpty(results)) {
            return Optional.empty();
        }

        // 获取所有错误信息
        String messages = results.stream()
                // 中间操作，迭代获取所有的错误消息
                .map(n -> String.format("%s: %s", n.getPropertyPath(), n.getMessage()))
                // 归约实现字符串合并
                .reduce((m1, m2) -> String.format("%s, %s", m1, m2))
                // 如果存在则返回值，否则返回other
                .orElse("参数输入有误");

        log.debug("实体验证结果，messages={}", messages);
        return Optional.of(messages);
    }
}
